package com.primestone.cim.client.app.window;

import java.io.PrintWriter;
import java.util.List;

import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.swt.widgets.Text;

import ch.iec.tc57._2011.getmeterreadingsmessage.MeterReadingsPayloadType;
import ch.iec.tc57._2011.meterreadings.GetMeterReadingsPort;
import ch.iec.tc57._2011.meterreadings.MeterReading;
import ch.iec.tc57._2011.meterreadings.MeterReading.IntervalBlocks;
import ch.iec.tc57._2011.meterreadings.MeterReading.IntervalBlocks.IntervalReadings;
import ch.iec.tc57._2011.meterreadings.MeterReading.Readings;
import ch.iec.tc57._2011.meterreadings.MeterReadings;
import ch.iec.tc57._2011.meterreadings.clientsample.ClientSample;

import com.primestone.cim.client.business.CSVBean;
import com.primestone.cim.client.business.WindowConstants;

/**
 * 
 * @author cesar.mateus
 * Jul 22, 2013 9:50:53 PM
 * LongRunningOperation.java
 * CIMClientApplication
 *
 */
class LongRunningOperation extends Thread {

	private Display display;

	private String vartypeRequest;
	private GetMeterReadingsPort port1;
	private int yearIni, monthIni, dayIni, yearEnd, monthEnd, dayEnd;
	private boolean isM, isR, isV, onlyNew; 
	private String descVar, socket, device; 
	private Integer top;
	private Table table;
	private Text error;
	private PrintWriter writer = null;	

	private boolean isLastBlock = false;
	private boolean noBlock = true;
	private String ticket;
	private String idBlock = null;
	private String lastBlock = null;
	private MeterReadingsPayloadType payload = null;

	private static final int SLEEP = 10000;

	private Options processType;

	public LongRunningOperation() {}

	public LongRunningOperation(LongRunningOperationParam param, Options processType, GetMeterReadingsPort port1, Display display, Table table, Text error) {
		initializeWriter();

		this.display = display;
		//this.progressBar = progressBar;

		this.vartypeRequest = param.getVartypeRequest();
		this.port1 = port1;
		this.yearIni = param.getYearIni(); 
		this.monthIni = param.getMonthIni(); 
		this.dayIni = param.getDayIni();
		this.yearEnd = param.getYearEnd(); 
		this.monthEnd = param.getMonthEnd(); 
		this.dayEnd = param.getDayEnd();
		this.isM = param.isM();
		this.isR = param.isR(); 
		this.isV = param.isV(); 
		this.onlyNew = param.isOnlyNew();
		this.descVar = param.getDescvar(); 
		this.socket = param.getSocket();
		this.device = param.getDevice(); 
		this.top = param.getTop();
		this.table = table;
		this.error = error;
		this.processType = processType;
	}

	void doInitialRequest() {
		try {

			initializeWriter();

			this.ticket = ClientSample.doRequestWithoutTicket(getPort1(), 
					this.yearIni, this.monthIni, this.dayIni,
					this.yearEnd, this.monthEnd, this.dayEnd,
					this.isM, this.isR, this.isV,
					this.descVar, this.socket, this.device, this.top, this.onlyNew);
			
			putScheduledDisplay();

			this.writer.println(new CSVBean(
					this.ticket, WindowConstants.EMPTY, WindowConstants.FALSE, WindowConstants.EMPTY, this.device, this.socket, this.vartypeRequest, this.descVar, 
					WindowConstants.FALSE, WindowConstants.EMPTY, WindowConstants.EMPTY, WindowConstants.EMPTY).getConcat());

			this.isLastBlock = false;
			this.payload = null;
			this.idBlock = null;
			this.lastBlock = null;
			this.noBlock = true;

			Object[] objects;
			while (this.noBlock) {
				objects = ClientSample.doRequestWithTicket(this.port1, this.ticket, this.socket, this.device);

				this.payload = (MeterReadingsPayloadType)objects[0];
				this.idBlock = (String)objects[1];
				this.lastBlock = (String)objects[2];
				this.isLastBlock = this.lastBlock != null;
				this.noBlock = this.idBlock.equals(WindowConstants.NOBLOCK);

				System.out.println("--- idBlock: " + this.idBlock + "--- lastBlock: " + this.lastBlock);
				if (this.noBlock) {
					Thread.sleep(SLEEP);
					System.out.println("---- Aun no se ha procesado el primer bloque de resultados.");
				}
			}
			
			putBlockDisplay();

			this.writer.println(new CSVBean(this.ticket, this.idBlock, this.isLastBlock, WindowConstants.EMPTY, this.device, this.socket, 
					WindowConstants.EMPTY, this.descVar, WindowConstants.FALSE, WindowConstants.EMPTY, WindowConstants.EMPTY, WindowConstants.EMPTY).getConcat());

			// Pintar el payload del primer bloque
			if (this.payload != null) {
				printPayload(this.payload, this.writer, this.ticket, this.idBlock, this.isLastBlock, this.device, this.socket, this.descVar);
			}

		} catch (final Exception ex) {
			ex.printStackTrace();
			putErrorDisplay(ex);
		} finally {
			if (this.isLastBlock) {
				if (this.writer != null) {
					this.writer.flush();
					this.writer.close();
				}
			}
		}
	}

	public void nextRequest() {
		try {
			if (!this.isLastBlock) {
				System.out.println("---- isLastBlock: " + this.isLastBlock);
				this.noBlock = true;
				Object[] objects;
				while (this.noBlock) {
					objects = ClientSample.doRequestWithTicket(this.port1, this.ticket, Long.valueOf(this.idBlock), this.socket, this.device);

					this.payload = (MeterReadingsPayloadType)objects[0];
					String idBlockTmp = (String)objects[1];
					this.lastBlock = (String)objects[2];
					this.isLastBlock = this.lastBlock != null;
					this.noBlock = idBlockTmp.equals(WindowConstants.NOBLOCK);

					System.out.println("--- idBlock: " + this.idBlock + "--- lastBlock: " + this.lastBlock);

					if (this.noBlock) {
						Thread.sleep(SLEEP);
					} else {
						this.idBlock = idBlockTmp;
					}
				}

				putBlockDisplay();

				// Pintar el payload de cada uno de los siguientes bloques
				if (this.payload != null) {
					printPayload(this.payload, this.writer, this.ticket, this.idBlock, this.isLastBlock, this.device, this.socket, this.descVar);
				}	
			}

			if (this.isLastBlock) {
				putLastDisplay();
			} else if (this.payload == null) {
				putErrorDisplay("Not found.");
			}
		} catch (final Exception ex) {
			ex.printStackTrace();
			putErrorDisplay(ex);
		} finally {
			if (this.isLastBlock) {
				if (this.writer != null) {
					this.writer.flush();
					this.writer.close();
				}	
			}
		}
	}

	void nextAllRequest() {
		try {			
			Object[] objects;
			String idBlockTmp;
			while (!this.isLastBlock) {
				System.out.println("---- isLastBlock: " + this.isLastBlock);
				this.noBlock = true;
				while (this.noBlock) {
					objects = ClientSample.doRequestWithTicket(this.port1, this.ticket, Long.valueOf(this.idBlock), this.socket, this.device);

					this.payload = (MeterReadingsPayloadType)objects[0];
					idBlockTmp = (String)objects[1];
					this.lastBlock = (String)objects[2];
					this.isLastBlock = this.lastBlock != null;
					this.noBlock = idBlockTmp.equals(WindowConstants.NOBLOCK);

					System.out.println("--- idBlock: " + this.idBlock + "--- lastBlock: " + this.lastBlock);

					if (this.noBlock) {
						Thread.sleep(SLEEP);
					} else {
						this.idBlock = idBlockTmp;
					}
				}
				
				putBlockDisplay();


				System.out.println("Ticket: " + this.ticket +  " (block " + this.idBlock + ").");

				// Pintar el payload de cada uno de los siguientes bloques
				if (this.payload != null) {
					printPayload(this.payload, this.writer, this.ticket, this.idBlock, this.isLastBlock, this.device, this.socket, this.descVar);
				}

				if (this.isLastBlock) {
					putLastDisplay();
				}

				if (this.isLastBlock) {
					System.out.println("This is the last block.");
				}

			}
		} catch (final Exception ex) {
			ex.printStackTrace();
			putErrorDisplay(ex);
		} finally {
			if (this.isLastBlock) {
				if (this.writer != null) {
					this.writer.flush();
					this.writer.close();
				}	
			}
		}
	}

	void doBusiness() {
		try {
			initializeWriter();

			this.ticket = ClientSample.doRequestWithoutTicket(this.port1, 
					this.yearIni, this.monthIni, this.dayIni,
					this.yearEnd, this.monthEnd, this.dayEnd,
					this.isM, this.isR, this.isV,
					this.descVar, this.socket, this.device, this.top, this.onlyNew);

			putScheduledDisplay();

			this.writer.println(new CSVBean(this.ticket, WindowConstants.EMPTY, WindowConstants.FALSE, WindowConstants.EMPTY, this.device, this.socket, this.vartypeRequest, this.descVar, WindowConstants.FALSE, WindowConstants.EMPTY, WindowConstants.EMPTY, WindowConstants.EMPTY).getConcat());

			this.isLastBlock = false;
			this.payload = null;
			this.idBlock = null;
			this.lastBlock = null;
			this.noBlock = true;
			Object[] objects;
			while (this.noBlock) {
				objects = ClientSample.doRequestWithTicket(this.port1, this.ticket, this.socket, this.device);

				this.payload = (MeterReadingsPayloadType)objects[0];
				this.idBlock = (String)objects[1];
				this.lastBlock = (String)objects[2];
				this.isLastBlock = this.lastBlock != null;
				this.noBlock = this.idBlock.equals(WindowConstants.NOBLOCK);

				System.out.println("--- idBlock: " + this.idBlock + "--- lastBlock: " + this.lastBlock);
				//final String idBlockTmp = idBlock;
				if (this.noBlock) {
					Thread.sleep(SLEEP);
				}
			}
			
			putBlockDisplay();

			this.writer.println(new CSVBean(this.ticket, this.idBlock, this.isLastBlock, WindowConstants.EMPTY, this.device, this.socket, WindowConstants.EMPTY, this.descVar, WindowConstants.FALSE, WindowConstants.EMPTY, WindowConstants.EMPTY, WindowConstants.EMPTY).getConcat());

			// Pintar el payload del primer bloque
			if (this.payload != null) {
				printPayload(this.payload, this.writer, this.ticket, this.idBlock, this.isLastBlock, this.device, this.socket, this.descVar);
			}

			System.out.println("---- isLastBlock: " + this.isLastBlock);
			String idBlockTmp;
			while (!this.isLastBlock) {
				this.noBlock = true;
				while (this.noBlock) {
					objects = ClientSample.doRequestWithTicket(this.port1, this.ticket, Long.valueOf(this.idBlock), this.socket, this.device);

					this.payload = (MeterReadingsPayloadType)objects[0];
					idBlockTmp = (String)objects[1];
					this.lastBlock = (String)objects[2];
					this.isLastBlock = this.lastBlock != null;
					this.noBlock = idBlockTmp.equals(WindowConstants.NOBLOCK);

					System.out.println("--- idBlock: " + this.idBlock + "--- lastBlock: " + this.lastBlock);
					//final String idBlockPaint = idBlock;

					if (this.noBlock) {
						Thread.sleep(SLEEP);
					} else {
						this.idBlock = idBlockTmp;
					}
				}
				
				putBlockDisplay();

				// Pintar el payload de cada uno de los siguientes bloques
				if (this.payload != null) {
					printPayload(this.payload, this.writer, this.ticket, this.idBlock, this.isLastBlock, this.device, this.socket, this.descVar);
				}

				if (this.isLastBlock) {
					putLastDisplay();
				}
			}
		} catch (final Exception ex) {
			ex.printStackTrace();
			putErrorDisplay(ex);
		} finally {
			if (this.writer != null) {
				this.writer.flush();
				this.writer.close();
			}
		}
	}

	private static void printPayload (MeterReadingsPayloadType payload, PrintWriter writer, 
			String ticket, String idBlock, Boolean isLastBlock, String device, String socket, String descvar) {
		MeterReadings meterReadings = payload.getMeterReadings();
		List<MeterReading> meterReading = meterReadings.getMeterReading();
		List<IntervalBlocks> intervalBlocks;
		List<IntervalReadings> intervalReadings;
		List<Readings> readings;
		for (MeterReading mReading : meterReading) {
			intervalBlocks = mReading.getIntervalBlocks();
			for (IntervalBlocks intervalBlock : intervalBlocks) {
				writer.println(new CSVBean(ticket, idBlock, isLastBlock, WindowConstants.EMPTY, device, socket, WindowConstants.EMPTY, descvar, WindowConstants.TRUE, WindowConstants.EMPTY, WindowConstants.EMPTY, WindowConstants.EMPTY).getConcat());				
				intervalReadings = intervalBlock.getIntervalReadings();
				for (IntervalReadings intervalReading : intervalReadings) {
					writer.println(new CSVBean(ticket, idBlock, isLastBlock, WindowConstants.EMPTY, device, socket, WindowConstants.EMPTY, descvar, 
							WindowConstants.TRUE, intervalReading.getTimeStamp().toString(), intervalReading.getValue().toString(), intervalBlock.getReadingType().getRef()).getConcat());					
				}
			}

			readings = mReading.getReadings();
			if (readings.size() > 0) {
				writer.println(new CSVBean(ticket, idBlock, isLastBlock, WindowConstants.EMPTY, device, socket, WindowConstants.EMPTY, descvar, WindowConstants.FALSE, WindowConstants.EMPTY, WindowConstants.EMPTY, WindowConstants.EMPTY).getConcat());
			}
			for (Readings reads : readings) {
				writer.println(new CSVBean(ticket, idBlock, isLastBlock, WindowConstants.EMPTY, device, socket, WindowConstants.EMPTY, descvar, 
						WindowConstants.FALSE, reads.getTimeStamp().toString(), reads.getValue().toString(), reads.getReadingType().getRef()).getConcat());
			}
		}
	}
	
	@Override
	public void run() {		
		if (getProcessType() != null) {
			switch (getProcessType()) {
				case ALL_PROCESS: {
					doBusiness();
					break;
				}
				case INITIAL_REQUEST: {
					doInitialRequest();
					break;
				}
				case ALL_NEXT: {
					nextAllRequest();
					break;
				}
			}	
		}
	}

	private void initializeWriter() {
		try {
			if (this.writer == null) {
				this.writer = new PrintWriter(WindowConstants.LOG_FILE);
				this.writer.println(WindowConstants.HEADER_FILE);	
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	private void putBlockDisplay() {
		this.display.asyncExec(new Runnable() {
			@Override
			public void run() {
				if (LongRunningOperation.this.table.isDisposed ()) return;
				TableItem item = new TableItem(LongRunningOperation.this.table, SWT.NONE);
				item.setText("Ticket: " + LongRunningOperation.this.ticket +  " (block " + LongRunningOperation.this.idBlock + ").");		
			}
		});
	}
	
	private void putScheduledDisplay() {
		this.display.asyncExec(new Runnable() {
			@Override
			public void run() {
				if (LongRunningOperation.this.table.isDisposed ()) return;
				TableItem item = new TableItem(LongRunningOperation.this.table, SWT.NONE);
				item.setText("Ticket: " + LongRunningOperation.this.ticket +  " (Scheduled)");		
			}
		});
	}
	
	private void putErrorDisplay(final Exception ex) {
		this.display.asyncExec(new Runnable() {
			@Override
			public void run() {
				LongRunningOperation.this.error.setText(ex.getMessage());		
			}
		});
	}
	
	private void putErrorDisplay(final String msg) {
		this.display.asyncExec(new Runnable() {
			@Override
			public void run() {
				LongRunningOperation.this.error.setText(msg);		
			}
		});
	}
	
	private void putLastDisplay() {
		this.display.asyncExec(new Runnable() {
			@Override
			public void run() {
				LongRunningOperation.this.error.setText("This is the last block.");
				LongRunningOperation.this.error.setText("OK");		
			}
		});
	}

	/**
	 * @return the display
	 */
	public Display getDisplay() {
		return this.display;
	}

	/**
	 * @param display the display to set
	 */
	public void setDisplay(Display display) {
		this.display = display;
	}

	/**
	 * @return the vartypeRequest
	 */
	public String getVartypeRequest() {
		return this.vartypeRequest;
	}

	/**
	 * @param vartypeRequest the vartypeRequest to set
	 */
	public void setVartypeRequest(String vartypeRequest) {
		this.vartypeRequest = vartypeRequest;
	}

	/**
	 * @return the port1
	 */
	public GetMeterReadingsPort getPort1() {
		return this.port1;
	}

	/**
	 * @param port1 the port1 to set
	 */
	public void setPort1(GetMeterReadingsPort port1) {
		this.port1 = port1;
	}

	/**
	 * @return the yearIni
	 */
	public int getYearIni() {
		return this.yearIni;
	}

	/**
	 * @param yearIni the yearIni to set
	 */
	public void setYearIni(int yearIni) {
		this.yearIni = yearIni;
	}

	/**
	 * @return the monthIni
	 */
	public int getMonthIni() {
		return this.monthIni;
	}

	/**
	 * @param monthIni the monthIni to set
	 */
	public void setMonthIni(int monthIni) {
		this.monthIni = monthIni;
	}

	/**
	 * @return the dayIni
	 */
	public int getDayIni() {
		return this.dayIni;
	}

	/**
	 * @param dayIni the dayIni to set
	 */
	public void setDayIni(int dayIni) {
		this.dayIni = dayIni;
	}

	/**
	 * @return the yearEnd
	 */
	public int getYearEnd() {
		return this.yearEnd;
	}

	/**
	 * @param yearEnd the yearEnd to set
	 */
	public void setYearEnd(int yearEnd) {
		this.yearEnd = yearEnd;
	}

	/**
	 * @return the monthEnd
	 */
	public int getMonthEnd() {
		return this.monthEnd;
	}

	/**
	 * @param monthEnd the monthEnd to set
	 */
	public void setMonthEnd(int monthEnd) {
		this.monthEnd = monthEnd;
	}

	/**
	 * @return the dayEnd
	 */
	public int getDayEnd() {
		return this.dayEnd;
	}

	/**
	 * @param dayEnd the dayEnd to set
	 */
	public void setDayEnd(int dayEnd) {
		this.dayEnd = dayEnd;
	}

	/**
	 * @return the isM
	 */
	public boolean isM() {
		return this.isM;
	}

	/**
	 * @param isM the isM to set
	 */
	public void setM(boolean isM) {
		this.isM = isM;
	}

	/**
	 * @return the isR
	 */
	public boolean isR() {
		return this.isR;
	}

	/**
	 * @param isR the isR to set
	 */
	public void setR(boolean isR) {
		this.isR = isR;
	}

	/**
	 * @return the isV
	 */
	public boolean isV() {
		return this.isV;
	}

	/**
	 * @param isV the isV to set
	 */
	public void setV(boolean isV) {
		this.isV = isV;
	}

	/**
	 * @return the onlyNew
	 */
	public boolean isOnlyNew() {
		return this.onlyNew;
	}

	/**
	 * @param onlyNew the onlyNew to set
	 */
	public void setOnlyNew(boolean onlyNew) {
		this.onlyNew = onlyNew;
	}

	/**
	 * @return the descVar
	 */
	public String getDescVar() {
		return this.descVar;
	}

	/**
	 * @param descVar the descVar to set
	 */
	public void setDescVar(String descVar) {
		this.descVar = descVar;
	}

	/**
	 * @return the socket
	 */
	public String getSocket() {
		return this.socket;
	}

	/**
	 * @param socket the socket to set
	 */
	public void setSocket(String socket) {
		this.socket = socket;
	}

	/**
	 * @return the device
	 */
	public String getDevice() {
		return this.device;
	}

	/**
	 * @param device the device to set
	 */
	public void setDevice(String device) {
		this.device = device;
	}

	/**
	 * @return the top
	 */
	public Integer getTop() {
		return this.top;
	}

	/**
	 * @param top the top to set
	 */
	public void setTop(Integer top) {
		this.top = top;
	}

	/**
	 * @return the table
	 */
	public Table getTable() {
		return this.table;
	}

	/**
	 * @param table the table to set
	 */
	public void setTable(Table table) {
		this.table = table;
	}

	/**
	 * @return the error
	 */
	public Text getError() {
		return this.error;
	}

	/**
	 * @param error the error to set
	 */
	public void setError(Text error) {
		this.error = error;
	}

	/**
	 * @return the isLastBlock
	 */
	public boolean isLastBlock() {
		return this.isLastBlock;
	}

	/**
	 * @param isLastBlock the isLastBlock to set
	 */
	public void setLastBlock(boolean isLastBlock) {
		this.isLastBlock = isLastBlock;
	}

	/**
	 * @return the processType
	 */
	public Options getProcessType() {
		return this.processType;
	}

	/**
	 * @param processType the processType to set
	 */
	public void setProcessType(Options processType) {
		this.processType = processType;
	}

	/**
	 * @return the writer
	 */
	public PrintWriter getWriter() {
		return this.writer;
	}

	/**
	 * @param writer the writer to set
	 */
	public void setWriter(PrintWriter writer) {
		this.writer = writer;
	}

	/**
	 * @return the ticket
	 */
	public String getTicket() {
		return this.ticket;
	}

	/**
	 * @param ticket the ticket to set
	 */
	public void setTicket(String ticket) {
		this.ticket = ticket;
	}

	/**
	 * @return the idBlock
	 */
	public String getIdBlock() {
		return this.idBlock;
	}

	/**
	 * @param idBlock the idBlock to set
	 */
	public void setIdBlock(String idBlock) {
		this.idBlock = idBlock;
	}

	/**
	 * @return the lastBlock
	 */
	public String getLastBlock() {
		return this.lastBlock;
	}

	/**
	 * @param lastBlock the lastBlock to set
	 */
	public void setLastBlock(String lastBlock) {
		this.lastBlock = lastBlock;
	}


	public static enum Options {
		ALL_PROCESS, INITIAL_REQUEST, ALL_NEXT;
	}
}